home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 February / EnigmA AMIGA RUN 15 (1997)(G.R. Edizioni)(IT)[!][issue 1997-02][PLANET CD V].iso / enigma / earcd / sviluppo / svilupp2 / amphn192.lha / src / messages.c < prev    next >
C/C++ Source or Header  |  1996-11-16  |  17KB  |  558 lines

  1. /* AmiPhone!  by Jeremy Friesner - jfriesne@ucsd.edu */
  2.  
  3. #define INTUI_V36_NAMES_ONLY
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h> 
  7. #include <string.h>
  8.  
  9. #include <dos/dos.h>
  10. #include <dos/dostags.h>
  11. #include <devices/audio.h>
  12. #include <intuition/intuition.h>
  13. #include <intuition/intuitionbase.h>
  14. #include <intuition/gadgetclass.h>
  15. #include <intuition/screens.h>
  16. #include <libraries/gadtools.h>
  17. #include <dos/dosextens.h>
  18. #include <exec/ports.h>
  19. #include <exec/memory.h>
  20. #include <exec/types.h>
  21. #include <exec/tasks.h>
  22. #include <exec/io.h>
  23. #include <exec/libraries.h>
  24. #include <libraries/dos.h>            /* contains RETURN_OK, RETURN_WARN #def's */
  25. #include <graphics/gfxbase.h>
  26. #include <libraries/gadtools.h>
  27. #include <libraries/iffparse.h>
  28. #include <sys/types.h>
  29.  
  30. #include <errno.h>
  31. #include <inetd.h>
  32.  
  33. #include <clib/alib_protos.h>
  34. #include <clib/dos_protos.h>
  35. #include <clib/exec_protos.h>
  36. #include <clib/iffparse_protos.h>
  37. #include <clib/intuition_protos.h>
  38. #include <clib/graphics_protos.h>
  39. #include <clib/gadtools_protos.h>
  40. #include <clib/diskfont_protos.h>
  41. #include <clib/iffparse_protos.h>
  42.  
  43. #include "toccata/include/libraries/toccata.h"
  44. #include "toccata/include/clib/toccata_protos.h"
  45. #include "toccata/include/pragmas/toccata_pragmas.h"
  46.  
  47. #include "AmiPhone.h"
  48. #include "AmiPhonePacket.h"
  49. #include "asl.h"
  50. #include "codec.h"
  51. #include "messages.h"
  52. #include "browse.h"
  53.  
  54. #define MAGIC_WORD "APHN"
  55. #define PLAYBUFSIZE (MAXTRANSBUFSIZE*4)
  56.  
  57. #define AMIPHONE_FILE_RATE  -1
  58. #define UNKNOWN_FILE_RATE    0
  59.  
  60. /* `VHDR' header format. */
  61. struct Voice8Header
  62. {
  63.         ULONG   oneShotHiSamples,       /* # samples in the high octave 1-shot part */
  64.                 repeatHiSamples,        /* # samples in the high octave repeat part */
  65.                 samplesPerHiCycle;      /* # samples/cycle in high octave, else 0 */
  66.         UWORD   samplesPerSec;          /* data sampling rate */
  67.         UBYTE   ctOctave,               /* # of octaves of waveforms */
  68.                 sCompression;           /* data compression technique used */
  69.         LONG    volume;                 /* playback nominal volume from 0 to Unity
  70.                                          * (full volume). Map this value into
  71.                                          * the output hardware's dynamic range.
  72.                                          */
  73. };
  74.  
  75.  
  76. /* private functions */
  77. static int LoadBuffer(struct MsgPort * STReplyPort, UBYTE * pBuf, int nBufNum, UBYTE * pubPlayBuf, struct AmiPhonePacketHeader * pHeader, FILE * fpIn, struct IFFHandle * Handle);
  78. static BOOL PlayBuffer(int nAudioNum, UBYTE * pubData, struct AmiPhonePacketHeader * pHeader);
  79. static struct IFFHandle * Init8SVXLoad(char * szFileName);
  80.  
  81. extern UBYTE ubCurrComp;
  82. extern ULONG ulSampleArraySize;
  83. extern ULONG ulBytesPerSecond;
  84.  
  85. /* vars used by AllocAudio, etc. */
  86. extern struct GfxBase * GfxBase;
  87. struct MsgPort *port1, *port2;
  88. struct IOAudio *AIOptr1=NULL, *AIOptr2=NULL;
  89. extern struct Task * MainTask;
  90. extern BOOL BNetConnect, BXmitOnPlay;
  91. extern struct MsgPort * SoundTaskPort;
  92. extern char szVoiceMailDir[];
  93.  
  94. ULONG SystemClock;
  95. struct AmiPhonePacketHeader * TransferPacket;
  96.  
  97. __geta4 void SoundPlayerMain(void);
  98.  
  99. /* private vars */
  100. static char * szDefDir = NULL;
  101. static char * szFileToPlay = NULL;
  102. struct Library * AslBase = NULL; 
  103.  
  104. /* IFF parsing state */
  105. struct Library * IFFParseBase             = NULL; 
  106. static int nPlaybackRate;
  107.  
  108. /* Should only be called by AmiPhone.c! */
  109. /* That we can make sure only one is running at one time */
  110. struct Task * LaunchPlayer(char * szFile)
  111. {
  112.     int nLen = strlen(szFile)+1;
  113.     struct Task * player;
  114.     
  115.     if (szFileToPlay != NULL) 
  116.     {
  117.         printf("LaunchPlayer(): Error--szFileToPlay in use!\n");
  118.         return(NULL);    /* panic--someone else is playing?? */
  119.     }
  120.     
  121.     UNLESS(szFileToPlay = AllocMem(nLen, MEMF_ANY)) return(NULL);
  122.     Strncpy(szFileToPlay,szFile,nLen);
  123.  
  124.     UNLESS(player = CreateNewProcTags(
  125.         NP_Entry,    SoundPlayerMain,
  126.         NP_Name,    "AmiPhone Message Player",
  127.         NP_Priority,    0,
  128.         NP_Output,    stdout,
  129.         NP_CloseOutput, FALSE,
  130.         NP_Cli,        TRUE,
  131.         TAG_END)) 
  132.     {
  133.         printf("LaunchPlayer failed.\n");
  134.         FreeMem(szFileToPlay,nLen);
  135.         szFileToPlay = NULL;    
  136.     }
  137.     return(player);
  138. }
  139.  
  140.  
  141.  
  142. /* This tasks opens a file requester and if a file is selected, sends
  143.    a MESSAGE_PLAY_FILE to the SoundPort. */
  144. __geta4 void FileReqMain(void)
  145. {
  146.     char szFile[300];
  147.     struct MsgPort * FileReplyPort;
  148.     static BOOL BFirstTime = TRUE;
  149.         
  150.     AslBase = OpenLibrary("asl.library",37L);
  151.     if (FileReplyPort = CreatePort(0,0))
  152.     {
  153.         /* REQUEST A FILE--ONLY ALLOW ONE OF THESE REQUESTERS OPEN AT ONCE! */
  154.         if ((AslBase)&&(FileRequest("Select a sound file to play", szFile, NULL, BFirstTime ? szVoiceMailDir : NULL, NULL, FALSE)))
  155.         {
  156.             StopPlayer(FileReplyPort);
  157.             SendPlayerMessage(MESSAGE_CONTROLMAIN_PLAYFILE, (void *)szFile, 0L, FileReplyPort);
  158.             BFirstTime = FALSE;
  159.         }
  160.  
  161.         /* Get main to reenable our menu option */
  162.         SendPlayerMessage(MESSAGE_CONTROLMAIN_REQCLOSED, NULL, 0L, FileReplyPort);
  163.  
  164.         /* Remove the port */
  165.         RemovePortSafely(FileReplyPort);
  166.     }
  167.     if (AslBase) {CloseLibrary(AslBase); AslBase = NULL;}
  168. }
  169.  
  170.  
  171.  
  172.  
  173. void RemovePortSafely(struct MsgPort * RemoveMe)
  174. {
  175.     Forbid();
  176.     while(GetMsg(RemoveMe)) {}
  177.     DeletePort(RemoveMe);
  178.     Permit();
  179. }
  180.  
  181. struct Task * LaunchFileReq(char * szDir)
  182. {
  183.     return(CreateNewProcTags(
  184.         NP_Entry,    FileReqMain,
  185.         NP_Name,    "AmiPhone File Requester",
  186.         NP_Priority,    0,
  187.         NP_Output,    stdout,
  188.         NP_CloseOutput, FALSE,
  189.         NP_Arguments,   szDir,
  190.         NP_Cli,        TRUE,
  191.         TAG_END));
  192. }
  193.  
  194.  
  195.  
  196.  
  197. /* Play a file in the given format.  This function will start a new
  198.    process to play the given sound, and may be stopped by sending it
  199.    a control-C or a MESSAGE_DIE */
  200. __geta4 void SoundPlayerMain(void)
  201. {
  202.     char szBuf[5] = "\0\0\0\0\0";
  203.     UBYTE * playbuf1 = NULL, * playbuf2 = NULL, * pSendBuf = NULL;
  204.     BOOL BDone = FALSE, BAudioAlloced = FALSE;
  205.     struct AmiPhonePacketHeader header1, header2;
  206.     int nBufNum = 1, nErrors = 0;
  207.     FILE * fpIn;
  208.     ULONG ulWaitMask = 0L, signals;
  209.     struct MsgPort *SoundTaskReplyPort = NULL;
  210.     struct IFFHandle *Handle = NULL;
  211.  
  212.     /* default = raw file */
  213.     nPlaybackRate = UNKNOWN_FILE_RATE;
  214.     
  215.     UNLESS(szFileToPlay) goto Cleanup;
  216.     UNLESS(SoundTaskReplyPort = CreatePort(0,0)) 
  217.     {
  218.         printf("ERROR: Couldn't open Browser reply port!\n");
  219.         goto Cleanup;
  220.     }
  221.     
  222.     if (SetSignal(0L,0L) & SIGBREAKF_CTRL_C) goto Cleanup;
  223.     
  224.     UNLESS(fpIn = fopen(szFileToPlay,"rb"))
  225.     {
  226.         printf("PlayFile:  couldn't open file [%s]\n",szFileToPlay);
  227.         goto Cleanup;
  228.     }
  229.     fread(szBuf,4,1,fpIn);
  230.     if (strncmp(szBuf,MAGIC_WORD,4) == 0) nPlaybackRate = AMIPHONE_FILE_RATE;
  231.     else
  232.     {
  233.         fclose(fpIn); fpIn = NULL;
  234.         UNLESS(Handle = Init8SVXLoad(szFileToPlay)) 
  235.         {
  236.             /* last resort--reopen file as raw audio and play
  237.                back at the current sampling rate. */
  238.             nPlaybackRate = UNKNOWN_FILE_RATE;
  239.             UNLESS(fpIn = fopen(szFileToPlay,"rb")) goto Cleanup;
  240.         }
  241.     }
  242.     UNLESS(BAudioAlloced = AllocAudio(TRUE))
  243.     {
  244.         printf("PlayFile: couldn't Allocate Audio channel\n");
  245.         goto Cleanup;
  246.     }
  247.     /* Autoallocate initial load area if needed */
  248.     UNLESS(pSendBuf = AllocMem(sizeof(struct AmiPhoneSendBuffer), MEMF_ANY))
  249.     {
  250.         printf("SoundPlayer:  Couldn't allocate load space\n");
  251.         goto Cleanup;
  252.     }    
  253.     /* allocate playing buffers */
  254.     playbuf1 = AllocMem(PLAYBUFSIZE, MEMF_CHIP|MEMF_CLEAR);
  255.     if (playbuf1 == NULL)
  256.     {
  257.         printf("PlayFile:  couldn't allocate buffer 1\n");        
  258.         goto Cleanup;
  259.     }
  260.     playbuf2 = AllocMem(PLAYBUFSIZE, MEMF_CHIP|MEMF_CLEAR);    
  261.     if (playbuf2 == NULL)
  262.     {
  263.         printf("PlayFile:  couldn't allocate buffer 2\n");
  264.         goto Cleanup;
  265.     }
  266.  
  267.     /* set up our signals to wait on */
  268.     ulWaitMask |= (1<<port1->mp_SigBit) | (1<<port2->mp_SigBit) | SIGBREAKF_CTRL_C;
  269.  
  270.     if (SetSignal(0L,0L) & SIGBREAKF_CTRL_C) goto Cleanup;
  271.  
  272.     /* load both buffers to start out with */
  273.     nErrors += LoadBuffer(SoundTaskReplyPort, pSendBuf, nBufNum++, playbuf1, &header1, fpIn, Handle);
  274.     nErrors += LoadBuffer(SoundTaskReplyPort, pSendBuf, nBufNum++, playbuf2, &header2, fpIn, Handle);
  275.  
  276.     /* start by playing the first buffer */
  277.     PlayBuffer(1, playbuf1, &header1);  
  278.  
  279.     if (SetSignal(0L,0L) & SIGBREAKF_CTRL_C) goto Cleanup;
  280.     
  281.     /* play loop: a simple double-buffered load scheme */
  282.     while (nErrors < 2)
  283.     {
  284.         signals = Wait(ulWaitMask);
  285.         
  286.         if (signals & (1<<port1->mp_SigBit))    /* buffer 1 done playing? */
  287.         {
  288.             if (nErrors < 1) PlayBuffer(2, playbuf2, &header2);
  289.             nErrors += LoadBuffer(SoundTaskReplyPort, pSendBuf, nBufNum++, playbuf1, &header1, fpIn, Handle);
  290.         }
  291.         if (signals & (1<<port2->mp_SigBit))    /* buffer 2 done playing? */
  292.         {
  293.             if (nErrors < 1) PlayBuffer(1, playbuf1, &header1);
  294.             nErrors += LoadBuffer(SoundTaskReplyPort, pSendBuf, nBufNum++, playbuf2, &header2, fpIn, Handle);
  295.         }
  296.         
  297.         if (signals & SIGBREAKF_CTRL_C) nErrors=2;    /* Someone wants us dead */
  298.     }
  299.     
  300. /* clean up */
  301. Cleanup:
  302.          if (Handle != NULL) 
  303.          {
  304.              CloseIFF(Handle);
  305.         if (Handle->iff_Stream) Close(Handle->iff_Stream);
  306.         FreeIFF(Handle); 
  307.     }
  308.     if (IFFParseBase) {CloseLibrary(IFFParseBase); IFFParseBase = NULL;}
  309.     if (BAudioAlloced) AllocAudio(FALSE);
  310.     if (playbuf2 != NULL) {FreeMem(playbuf2, PLAYBUFSIZE);}
  311.     if (playbuf1 != NULL) {FreeMem(playbuf1, PLAYBUFSIZE);}
  312.     if (pSendBuf != NULL) {FreeMem(pSendBuf,sizeof(struct AmiPhoneSendBuffer));}
  313.     if (fpIn != NULL) fclose(fpIn);
  314.  
  315.     /* Free memory allocated in LaunchPlayer--hope this is legal! */
  316.     if (szFileToPlay) 
  317.     {
  318.         FreeMem(szFileToPlay,strlen(szFileToPlay));
  319.         szFileToPlay = NULL;
  320.     }
  321.  
  322.     /* Tell main program we're done--If SoundTaskReplyPort is NULL, we won't expect a reply */
  323.     /* It's very important that this message be sent, or main can't quit! */
  324.     SendPlayerMessage(MESSAGE_CONTROLMAIN_IMLEAVING, NULL, 0L, SoundTaskReplyPort);    
  325.  
  326.     /* Cleanup any remaining messages and close the port */
  327.     if (SoundTaskReplyPort) RemovePortSafely(SoundTaskReplyPort);
  328. }
  329.  
  330.  
  331.  
  332. /* Loads the next chunk of sound into sendbuf and pubPlayBuf.
  333.    Will compress/decompress the sound as necessary.
  334.    
  335.    Also copies the header into pHeader.  Returns 0 on success, 1
  336.    on failure.  pHeader->ulDataLen is set to the length of the decompressed
  337.    bytes in pubPlayBuf.  */
  338. static int LoadBuffer(struct MsgPort * STReplyPort, UBYTE * pBuf, int nBufNum, UBYTE * pubPlayBuf, struct AmiPhonePacketHeader * pHeader, FILE * fpIn, struct IFFHandle * Handle)
  339. {
  340.     ULONG ulBytesToRead = ulSampleArraySize<<2;
  341.     int nBytesRead;
  342.     static ULONG ulJoinCode;
  343.     
  344.     if (nBufNum == 1) ulJoinCode = 0L;
  345.     
  346.     if (nPlaybackRate == AMIPHONE_FILE_RATE)
  347.     {
  348.         /* load in packet header to get data size */
  349.         if ((fread(pHeader,1,sizeof(struct AmiPhonePacketHeader),fpIn)) < sizeof(struct AmiPhonePacketHeader))
  350.         {
  351.             if (nBufNum == 1) printf("LoadBuffer: Couldn't read file header.\n");
  352.             return(1);
  353.         }
  354.         /* do some sanity checking on our header */
  355.         if (pHeader->ubCommand != PHONECOMMAND_DATA)
  356.         {
  357.             printf("LoadBuffer:  chunk wasn't data! [%i]\n",pHeader->ubCommand);
  358.             return(1);
  359.         }
  360.         if ((pHeader->ubType < COMPRESS_NONE)||(pHeader->ubType >= COMPRESS_MAX))
  361.         {
  362.             printf("LoadBuffer:  Bad compression method! [%i]\n",pHeader->ubType);
  363.             return(1);
  364.         }
  365.         if ((pHeader->ulBPS < MIN_SAMPLE_RATE)||(pHeader->ulBPS > ABSOLUTE_MAX_SAMPLE_RATE))
  366.         {
  367.             printf("LoadBuffer:  Bad sampling rate! [%u]\n",pHeader->ulBPS);
  368.             return(1);
  369.         }
  370.  
  371.         /* copy the header to our load section */
  372.         memcpy(pBuf,pHeader,sizeof(struct AmiPhonePacketHeader));
  373.  
  374.         /* load in data to data section of our load buffer */
  375.         if ((nBytesRead = fread(pBuf+sizeof(struct AmiPhonePacketHeader), 1, pHeader->ulDataLen, fpIn)) < pHeader->ulDataLen)
  376.         {
  377.             pHeader->ulDataLen = (nBytesRead > 0) ? nBytesRead : 0;
  378.             return(1);
  379.         }
  380.         
  381.         /* If the main task cares, let it know that this packet should be sent */
  382.         if ((BNetConnect == TRUE)&&(BXmitOnPlay == TRUE)&&(STReplyPort))
  383.             SendPlayerMessage(MESSAGE_CONTROLMAIN_XMITPACKET, (void *)pBuf, 0L, STReplyPort);
  384.  
  385.         /* pHeader->ulDataLen will now contain # of bytes of raw audio data in pubPlayBuf */
  386.         pHeader->ulDataLen = DecompressData(pBuf+sizeof(struct AmiPhonePacketHeader),
  387.             pubPlayBuf, pHeader->ubType, pHeader->ulDataLen, pHeader->ulJoinCode);
  388.         return(0);
  389.     }
  390.     else
  391.     {
  392.         pHeader->ubCommand  = PHONECOMMAND_DATA;
  393.         pHeader->ubType     = ubCurrComp;
  394.         
  395.         if (nPlaybackRate == UNKNOWN_FILE_RATE) 
  396.         {
  397.             /* Raw data */
  398.             pHeader->ulBPS = ulBytesPerSecond;
  399.             nBytesRead = fread(pubPlayBuf,1,ulBytesToRead,fpIn);
  400.         }
  401.         else
  402.         {    
  403.             /* 8SVX file format */
  404.             pHeader->ulBPS = nPlaybackRate;
  405.             nBytesRead = ReadChunkBytes(Handle,pubPlayBuf,ulBytesToRead);
  406.         }
  407.         if (nBytesRead < 0) {pHeader->ulDataLen = 0; return(1);}
  408.         pHeader->ulDataLen = nBytesRead;
  409.         
  410.         memcpy(pBuf,pHeader,sizeof(struct AmiPhonePacketHeader));
  411.  
  412.         /* If the main task cares, let it know that this packet should be sent */
  413.         if ((BNetConnect == TRUE)&&(BXmitOnPlay == TRUE)&&(STReplyPort)&&(nBytesRead > 0))
  414.         {
  415.             struct AmiPhonePacketHeader * pCompHeader = (struct AmiPhonePacketHeader *) pBuf;
  416.             
  417.             pHeader->ulJoinCode = pCompHeader->ulJoinCode = ulJoinCode;
  418.             
  419.             pCompHeader->ulDataLen = CompressData(pubPlayBuf, 
  420.                 pBuf+sizeof(struct AmiPhonePacketHeader), 
  421.                 pHeader->ubType, pHeader->ulDataLen, 
  422.                 &ulJoinCode);
  423.             SendPlayerMessage(MESSAGE_CONTROLMAIN_XMITPACKET, (void *)pBuf, 0L, STReplyPort);
  424.         }
  425.         return(nBytesRead < ulBytesToRead);
  426.     }
  427. }
  428.  
  429.  
  430. /* Synchronously sends a message to the main program... i.e. this won't
  431.    return until the message is acknowledged.  Returns TRUE if the message
  432.    was successfully sent. */
  433. BOOL SendPlayerMessage(UBYTE ubControl, void * data, ULONG ulData2, struct MsgPort * WaitForReplyAt)
  434. {    
  435.     struct PlayerMessage * SoundTaskMessage = (struct PlayerMessage *) AllocMem(sizeof(struct PlayerMessage), MEMF_PUBLIC|MEMF_CLEAR);
  436.     BOOL BMessageSent;
  437.     
  438.     UNLESS((SoundTaskMessage)&&(WaitForReplyAt)) return(FALSE);
  439.     
  440.     /* Initialize our message struct */
  441.     SoundTaskMessage->Message.mn_Node.ln_Type = NT_MESSAGE;
  442.     SoundTaskMessage->Message.mn_Length       = sizeof(struct PlayerMessage);
  443.     SoundTaskMessage->Message.mn_ReplyPort    = WaitForReplyAt;
  444.     
  445.     /* Set data in message */
  446.     SoundTaskMessage->ubControl = ubControl;
  447.     SoundTaskMessage->data = data;
  448.     SoundTaskMessage->ulData2 = ulData2;
  449.     
  450.     /* Send it the message */
  451.     BMessageSent = FALSE;
  452.     Forbid();
  453.     if (SoundTaskPort)
  454.     {
  455.         PutMsg(SoundTaskPort, (struct Message *)SoundTaskMessage);
  456.         BMessageSent = TRUE;
  457.     }
  458.     Permit();
  459.             
  460.     if (BMessageSent == TRUE)
  461.     {
  462.         /* Wait for reply */
  463.         WaitPort(WaitForReplyAt);
  464.         GetMsg(WaitForReplyAt);
  465.     }
  466.  
  467.     /* deallocate memory */
  468.     FreeMem(SoundTaskMessage, sizeof(struct PlayerMessage));
  469.     
  470.     return(BMessageSent);
  471. }
  472.  
  473.  
  474. /*  starts the given buffer playing */
  475. static BOOL PlayBuffer(int nAudioNum, UBYTE * pubData, struct AmiPhonePacketHeader * pHeader)
  476. {
  477.     struct IOAudio * AIOptr;
  478.  
  479.     if (pHeader->ulBPS < MIN_SAMPLE_RATE) 
  480.     {
  481.         printf("PlayBuffer:  play rate too low [%d]\n",pHeader->ulBPS);
  482.         return(FALSE);
  483.     }
  484.     if (pHeader->ulDataLen < 1L) return(FALSE);
  485.  
  486.     
  487.          if (nAudioNum == 1) AIOptr = AIOptr1; 
  488.     else if (nAudioNum == 2) AIOptr = AIOptr2;
  489.     else return(FALSE);
  490.  
  491.     
  492.     /* Fill out sample length, pointer to data, and playback speed */    
  493.     AIOptr->ioa_Length = pHeader->ulDataLen;
  494.     AIOptr->ioa_Data   = pubData;
  495.     AIOptr->ioa_Period = (UWORD) (SystemClock / pHeader->ulBPS);    
  496.     
  497.     BeginIO((struct IORequest *)AIOptr);    /* start playing sample */
  498.     return(TRUE);
  499. }
  500.  
  501.  
  502.  
  503. /* Returns a valid IFFHandle if the 8SVX file can be read, or NULL on failure. */
  504. static struct IFFHandle * Init8SVXLoad(char * szFileName)
  505. {
  506.     struct StoredProperty *Prop        = NULL;
  507.     struct Voice8Header   *VoiceHeader    = NULL;
  508.     struct IFFHandle      *Handle        = NULL;
  509.     struct ContextNode    *ContextNode    = NULL;
  510.  
  511.     UNLESS(IFFParseBase = OpenLibrary("iffparse.library",37L)) return(NULL);
  512.  
  513.     /* Allocate an IFF handle. */
  514.     UNLESS(Handle = AllocIFF()) goto Init8SVXFail;
  515.  
  516.     /* Open the sound file for reading. */
  517.     UNLESS(Handle->iff_Stream = (ULONG) Open(szFileName,MODE_OLDFILE)) goto Init8SVXFail;
  518.  
  519.     InitIFFasDOS(Handle);
  520.         
  521.     /* Open the file for reading.  Zero return value == success */
  522.     if (OpenIFF(Handle,IFFF_READ)) goto Init8SVXFail;
  523.         
  524.     /* Remember the voice header chunk if encountered.  zero == success */
  525.     if (PropChunk(Handle,'8SVX','VHDR')) goto Init8SVXFail;
  526.     
  527.     /* Stop in front of the data body chunk. */
  528.     if (StopChunk(Handle,'8SVX','BODY')) goto Init8SVXFail;
  529.           
  530.     /* Scan the file... */
  531.     if (ParseIFF(Handle, IFFPARSE_SCAN)) goto Init8SVXFail;
  532.     
  533.     /* Try to find the voice header chunk. */
  534.     UNLESS(Prop = FindProp(Handle,'8SVX','VHDR')) goto Init8SVXFail;
  535.     VoiceHeader = (struct Voice8Header *)Prop -> sp_Data;
  536.     
  537.     /* No compression and only a single octave, please! */           
  538.     if (VoiceHeader->sCompression)
  539.     {
  540.         printf("PlaySound:  Sorry, compressed 8SVX isn't implemented!  :(\n");
  541.         goto Init8SVXFail;
  542.     }    
  543.     /* Get information on the current chunk. */
  544.     UNLESS(ContextNode = CurrentChunk(Handle)) goto Init8SVXFail;
  545.     nPlaybackRate = VoiceHeader->samplesPerSec;
  546.     return(Handle);
  547.  
  548. Init8SVXFail:
  549.     if (Handle) 
  550.     {
  551.         CloseIFF(Handle);
  552.         if (Handle->iff_Stream) Close(Handle->iff_Stream);
  553.         FreeIFF(Handle); Handle = NULL;
  554.     }
  555.     return(NULL);
  556. }
  557.  
  558.